
/*
  Author:
  Lu Yiming <lu.yiming.lu@gmail.com>
  Copy codes from 'The GNU C Library' manual, for 'udp local'.
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <stddef.h>
#include <time.h>
#include <math.h>

#include "usage.h"
#include "udp_local.h"
#include "data.h"

int make_named_socket (const char *filename)
{
	struct sockaddr_un name;
	int sock;
	size_t size;
     
	/* Create the socket. */
	sock = socket (PF_LOCAL, SOCK_DGRAM, 0);
	if (sock < 0)
	{
		perror ("socket");
		exit (EXIT_FAILURE);
	}
     
	/* Bind a name to the socket. */
	name.sun_family = AF_LOCAL;
	strncpy (name.sun_path, filename, sizeof (name.sun_path));
	name.sun_path[sizeof (name.sun_path) - 1] = '\0';
     
	/* The size of the address is
	   the offset of the start of the filename,
	   plus its length,
	   plus one for the terminating null byte.
	   Alternatively you can just do:
	   size = SUN_LEN (&name);
	*/
	size = (offsetof (struct sockaddr_un, sun_path)
		+ strlen (name.sun_path) + 1);
     
	if (bind (sock, (struct sockaddr *) &name, size) < 0)
	{
		perror ("bind");
		exit (EXIT_FAILURE);
	}
     
	return sock;
}

void udp_local_server(int argc, char **argv)
{

#define SERVER  "/tmp/serversocket"
#define MAXMSG  512

	int sock;
	char message[MAXMSG];
	struct sockaddr_un name;
	socklen_t size;             /* FIXME: size_t */ 
	int nbytes;
	void *buf_recv;
	int old_argc = argc;
	int debug = 0;

	while (argc > 0) {
		if ((argc > 0) &&
		    (0 == strcmp(argv[0], "debug"))) {
			debug = 1;
			argc -= 1;
			argv += 1;
		}

		if (old_argc == argc) {
			usage();
			exit (EXIT_FAILURE);
		}
		old_argc = argc;
	}
     
	printf("udp local server started.\n");

	/* Remove the filename first, it's ok if the call fails */
	unlink (SERVER);

	buf_recv = malloc(2000);
     
	/* Make the socket, then loop endlessly. */
	sock = make_named_socket (SERVER);
	while (1)
	{
		/* Wait for a datagram. */
		size = sizeof (name);
		/* nbytes = recvfrom (sock, message, MAXMSG, 0,
		   (struct sockaddr *) & name, &size); */

		nbytes = recvfrom (sock, buf_recv, 2000, 0,
				   (struct sockaddr *) & name, &size);
		if (nbytes < 0)
		{
			perror ("recfrom (server)");
			exit (EXIT_FAILURE);
		}
     
		if (debug) {
			fprintf (stderr, "Server: got data, ID: %ld\n", *((unsigned long int *)buf_recv));
		}

		/* Give a diagnostic message. */
		/* fprintf (stderr, "Server: got message: %s\n", message);  */

		/* fprintf (stderr, "Server: got data, ID: %ld\n", *((unsigned long int *)buf_recv));
		 */

		if (*(((unsigned int *)buf_recv + 2)) & (1 << 31)) {
			/* bit 31 means full-deplex */

			/* Bounce the message back to the sender. */
			nbytes = sendto (sock, buf_recv, nbytes, 0,
					 (struct sockaddr *) & name, size);
			if (nbytes < 0)
			{
				perror ("sendto (server)");
				exit (EXIT_FAILURE);
			}
		}
	}
}

void udp_local_client (int argc, char **argv)
{

#define SERVER  "/tmp/serversocket"
#define CLIENT  "/tmp/mysocket"
#define MAXMSG  512
#define MESSAGE "Yow!!! Are we having fun yet?!?"

	extern int make_named_socket (const char *name);
	int sock;
	char message[MAXMSG];
	struct sockaddr_un name;
	size_t size;
	int nbytes;
	unsigned long int i;
	void *buf_send;
	void *buf_recv;
	time_t time_start;
	time_t time_end;
	unsigned long int packets = 128 * 1024;
	char duplex = 1;
	int old_argc = argc;

	while (argc > 0) {
		if ((argc > 1) &&
		    (0 == strcmp(argv[0], "packets"))) {
			packets = atoll(argv[1]);
			argc -= 2;
			argv += 2;
		}
		if ((argc > 0) &&
		    (0 == strcmp(argv[0], "full-duplex"))) {
			duplex = 2;
			argc -= 1;
			argv += 1;
		}
		if ((argc > 0) &&
		    (0 == strcmp(argv[0], "half-duplex"))) {
			duplex = 1;
			argc -= 1;
			argv += 1;
		}
		if (old_argc == argc) {
			usage();
			exit (EXIT_FAILURE);
		}
		old_argc = argc;
	}

	remove (CLIENT);

	/* Make the socket. */
	sock = make_named_socket (CLIENT);
     
	/* Initialize the server socket address. */
	name.sun_family = AF_LOCAL;
	strcpy (name.sun_path, SERVER);
	size = strlen (name.sun_path) + sizeof (name.sun_family);

	buf_send = get_data();
	if (!buf_send) {
		perror ("get_data");
		exit (EXIT_FAILURE);
	}

	buf_recv = malloc(2000); /* we don't make UDP packet size more than 1400 */

	time(&time_start);

	for (i = 0; i < packets; i++) {

		/* buf[0-7] = ID, 0...n */
		*((unsigned long int *)buf_send) = i;
		/* buf[8-11] = message size */
		*(((unsigned int *)buf_send + 2)) = (1024 - 8 - 4) | 
		                                    ((1 == duplex) ? 0 : 1 << 31); /* bit 31 means full-deplex */

		/* Send the datagram. */
		/* nbytes = sendto (sock, MESSAGE, strlen (MESSAGE) + 1, 0,
		   (struct sockaddr *) & name, size);*/

		nbytes = sendto (sock, buf_send, 1024, 0,
		   (struct sockaddr *) & name, size);
		
		if (nbytes < 0)
		{
			perror ("sendto (client)");
			exit (EXIT_FAILURE);
		}

		if (2 == duplex) {
			/* Wait for a reply. */
			/* nbytes = recvfrom (sock, message, MAXMSG, 0, NULL, 0); */
			nbytes = recvfrom (sock, buf_recv, 2000, 0, NULL, 0);
			if (nbytes < 0)
			{
				perror ("recfrom (client)");
				exit (EXIT_FAILURE);
			}
			if (1024 != nbytes)
			{
				perror ("recv size");
			}

			if (*((unsigned long int *)buf_recv) != i) {
				perror ("recv ID");
			}
			if ((*(((unsigned int *)buf_recv) + 2) & 0xffffff) != (1024 - 8 - 4)) {
				perror ("recv message size");
			}
		}
	}
     
	/* Print a diagnostic message. */
	/* fprintf (stderr, "Client: got message: %s\n", message); */

	time(&time_end);

	fprintf (stderr, 
		 "Client: done. \n"
		 "Time elapsed (seconds): %'.2f\n"
		 "Packet (1KB) sent%s: %ld\n"
		 "Speed: %'.2f MBps\n"
		 , difftime(time_end, time_start)
		 , (1 == duplex) ? " (half-duplex)" : " and received (full-duplex)"
		 , packets
		 , 1.0 * packets * duplex / 1024 / ((0.0 == difftime(time_end, time_start)) ? 1.0 : difftime(time_end, time_start)));
     
	/* Clean up. */
	remove (CLIENT);
	close (sock);
}
